package ru.ifmo.genetics.adaptation;

import org.uncommons.maths.number.NumberGenerator;
import org.uncommons.maths.random.Probability;
import org.uncommons.watchmaker.framework.EvaluatedCandidate;
import org.uncommons.watchmaker.framework.EvolutionUtils;
import ru.ifmo.automaton.Automaton;
import ru.ifmo.common.Source;
import ru.ifmo.genetics.automaton.synchronizeable.fitness.StandartFitness;

import java.util.List;

/**
 * @author Roman Kolganov
 *         10.12.11
 */
public class CorrectingProbabilityGenerator implements NumberGenerator<Probability>, AdaptableParameter<String> {

    private final NumberGenerator<Probability> initialProbability;
    private Probability currentProbability;
    private final Source<Automaton<Character, String>> automatonSource;
    private final NumberGenerator<Double> probabilityIncrease;

    public CorrectingProbabilityGenerator(NumberGenerator<Probability> initialProbability,
                                          Source<Automaton<Character, String>> automatonSource,
                                          NumberGenerator<Double> probabilityIncrease) {
        this.initialProbability = initialProbability;
        currentProbability = initialProbability.nextValue();
        this.automatonSource = automatonSource;
        this.probabilityIncrease = probabilityIncrease;
    }

    public void adapt(List<List<EvaluatedCandidate<String>>> lastGenerations) {
        EvolutionUtils.sortEvaluatedPopulation(lastGenerations.get(0), true);
        EvolutionUtils.sortEvaluatedPopulation(lastGenerations.get(1), true);
        if (maxDF(lastGenerations.get(0)) >= maxDF(lastGenerations.get(1))) {
            currentProbability = new Probability(Math.min(currentProbability.doubleValue() + probabilityIncrease.nextValue(), 1));
        } else {
            currentProbability = initialProbability.nextValue();
        }
    }

    private int maxDF(List<EvaluatedCandidate<String>> generation) {
        return StandartFitness.df(automatonSource.create(), generation.get(0).getCandidate());
    }

    public Probability nextValue() {
        return currentProbability;
    }

}
